/*
 * GeoVISTA Center (Penn State, Dept. of Geography)
 * Copyright (c), 2003-2011,  Jin Chen and Alan M. MacEachren, GeoVISTA Center, Penn State University
 * All Rights Researved.
 *
 * This library is developed and distributed for free usage,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * You can redistribute and/or modify it under the terms of the
 * Simplified BSD License
 *
 *
 *
 *
 *
 *
 *
 * Given a array , the animation will go through each element of the array in the same order as the array.
 * Each time it fire a event contain the value of the element. The event also contain information on if the animation is
 * to start, paused and stoped.
 *
 * @author: jin Chen
 * @date: Oct 16, 2003$
 * @version: 1.0
 */
package edu.psu.geovista.app.pcp.animation;

import edu.psu.geovista.app.pcp.animation.event.AnimationEvent;
import edu.psu.geovista.app.pcp.animation.event.AnimationListener;

import javax.swing.*;
import javax.swing.event.EventListenerList;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;

public class AnimationWorker implements ActionListener {
    public static final int Interative=1;//paint all data, one by one over others
    public static final int Ordered=2;//paint single data, one by one
    protected EventListenerList listenerList = new EventListenerList();
    //Timer
    private Timer timer;
    private int delay=1000;
    private boolean stepAnimationOn; //see if manual animation(step) is on the way


    private List data;  //the data for animation, if null, mean whole data

    int count=0;
    boolean ascend=true;

    public AnimationWorker() {
        setupTimer();
    }
    private void setupTimer(){
        timer=new Timer(delay,null);
        timer.setInitialDelay(0);
        timer.setCoalesce(true);
        this.timer.addActionListener(this);
    }
    /**
     * Listen to timer and do animation
     * @param e
     */
    public void actionPerformed(ActionEvent e) {

        doOneStepAnimation() ;
        /*if (anm==false){//animation end,
            this.stop() ;
        }*/
    }


    private  void doOneStepAnimation() {
        //DataSelectCmd dst=(DataSelectCmd) getCmd(BasicCmd.DataSelectT );
        boolean continues=true;
        if(!this.isStepAnimationOn() ){
            this.fireAnimationChanged(-1, AnimationEvent.ANIMATION_AboutToStart );
            this.setStepAnimationOn(true);
        }

        if(ascend){
            Integer recordId=(Integer) this.data.get(count++);
            int id=recordId.intValue() ;
            this.fireAnimationChanged(id, AnimationEvent.ANIMATION_ON );
            if (count>=data.size() ){
                    reset();
                    continues= false;
            }
        }
        else{

            Integer recordId=(Integer) this.data.get(count--);
            int id=recordId.intValue() ;
            this.fireAnimationChanged(id, AnimationEvent.ANIMATION_ON );
            if (count<0 ){
                    reset();
                    continues= false;
            }

        }

        if(continues==false){
            stop();
        }
    }
    public void start() {

        this.fireAnimationChanged(-1, AnimationEvent.ANIMATION_AboutToStart );
            //clean=false;
        //}
        this.startTimer() ;
    }
    public void pause() {
        this.stopTimer() ;
        this.fireAnimationChanged(-1, AnimationEvent.ANIMATION_Paused ); //-1 means invalid index value
    }
    public void stop() {
        this.stopTimer() ;
        this.reset() ;
        this.fireAnimationChanged(-1, AnimationEvent.ANIMATION_Stoped );
    }
    public void step() {
        if (timer.isRunning()) {
            pause();
        }
        doOneStepAnimation();
        //this.fireAnimationChanged(0, AnimationEvent.ANIMATION_ON );
    }
    private void reset(){
        if(ascend)
            this.count =0;
        else
            count=data.size()-1;

        this.setStepAnimationOn(false);
        //this.clean =true;
    }
    /*******************************************************************************************************
     *                Handle Timmer only, do put application logic ->
     *******************************************************************************************************/
    private  synchronized void startTimer() {

        if (!timer.isRunning()) {
            timer.start();
        }

    }
    //Can be invoked by any thread (since timer is thread-safe).
    private synchronized void stopTimer() {
        //Stop the animating thread.
        if (timer.isRunning()) {
            timer.stop();
        }
    }
    /*******************************************************************************************************
     *                Properties
     *******************************************************************************************************/

    public void setData(List data) {

        if (data==null){
            this.data = null;
            return;
        }
        this.data = data;
        //Collections.sort(this.data);


    }

    public void setDelay(int delay) {
        this.delay = delay;
        this.timer.setDelay(this.delay);
    }

    public boolean isAscend() {
        return ascend;
    }

    public void setAscend(boolean ascend) {
        this.ascend = ascend;
        if(ascend){
            this.count =0;//data is always in ascending order. If ascend, start with 1st data
        }
        else{
            count=this.data.size() -1; //data is always in ascending order. If ascend, start with last data
        }
    }

    public boolean isStepAnimationOn() {
        /*if(this.timer.isRunning() ){
            return true;

        }
        else*/
            return stepAnimationOn; //see if manual animation(step) is on the way
    }

    public void setStepAnimationOn(boolean stepAnimationOn) {
        this.stepAnimationOn = stepAnimationOn;
    }

    /*******************************************************************************************************
     *                event
     *******************************************************************************************************/

  public void addAnimationListener(AnimationListener l) {
    listenerList.add(AnimationListener.class, l);
  }

  public void removeAnimationListener(AnimationListener l) {
    listenerList.remove(AnimationListener.class, l);
  }

  private void fireAnimationChanged(int id, int state) {


    Object[] listeners = listenerList.getListenerList();
    AnimationEvent e = null;

    for (int i = listeners.length - 2; i >= 0; i -= 2) {
      if (listeners[i] == AnimationListener.class) {
        if (e == null) {
          e = new AnimationEvent(this, id,state);
        }

        ((AnimationListener) listeners[i + 1]).animationChanged(e);
      }
    }

 }



}
